// ShmLcReader_timed.cpp  —— 统计一次“读共享内存+取TSC+换算L_now”的耗时分布
#ifndef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 200809L
#endif
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <atomic>
#include <cstdint>
#include <cstdio>
#include <ctime>
#include <cmath>
#include <cstring>
#include <inttypes.h>
#include <vector>
#include <algorithm>

// 与写端完全一致的时间源
#include "time_source.h"   // 提供 TscSource::now_ns()

struct alignas(64) ShmLcMap {
    std::atomic<uint64_t> seq;  // 偶数=稳定，奇数=写入中（seqlock）
    uint64_t R_anchor_ns;
    int64_t  L_anchor_ns;
    double   s_ns_per_ns;
    uint32_t version;
    uint32_t _pad;
};

static inline bool seqlock_snapshot(const ShmLcMap* s, ShmLcMap& snap) {
    // 读者 seqlock：两次读 seq 包夹一次拷贝
    while (true) {
        uint64_t r1 = s->seq.load(std::memory_order_acquire);
        if (r1 & 1u) continue;  // 写入中，重试
        snap.R_anchor_ns = s->R_anchor_ns;
        snap.L_anchor_ns = s->L_anchor_ns;
        snap.s_ns_per_ns = s->s_ns_per_ns;
        snap.version     = s->version;
        uint64_t r2 = s->seq.load(std::memory_order_acquire);
        if (r1 == r2 && !(r2 & 1u)) return true;
        // 否则重来
    }
}

static inline int64_t to_logic_now(const ShmLcMap& snap, uint64_t R_now) {
    long double delta  = (long double)R_now - (long double)snap.R_anchor_ns;
    long double L_nowd = (long double)snap.L_anchor_ns + (long double)snap.s_ns_per_ns * delta;
    return (int64_t)llround(L_nowd);
}

static inline double percentile(std::vector<uint64_t>& v, double p) {
    if (v.empty()) return 0.0;
    size_t idx = (size_t)std::floor(p * (v.size() - 1) + 0.5);
    return (double)v[idx];
}

int main(int argc, char** argv) {
    const char* name = "/lc_map_v1";
    int N = 200000;          // 默认循环次数
    bool once = false;

    // 简单参数：--shm <name>  --n <N>  --once
    for (int i=1; i<argc; ++i) {
        if (!std::strcmp(argv[i], "--shm") && i+1<argc) name = argv[++i];
        else if (!std::strcmp(argv[i], "--n") && i+1<argc) N = std::max(1, atoi(argv[++i]));
        else if (!std::strcmp(argv[i], "--once")) once = true;
    }
    if (once) N = 1;

    // 只读映射共享内存
    int fd = shm_open(name, O_RDONLY, 0660);
    if (fd < 0) { perror("shm_open"); return 1; }

    void* p = mmap(nullptr, sizeof(ShmLcMap), PROT_READ, MAP_SHARED, fd, 0);
    if (p == MAP_FAILED) { perror("mmap"); close(fd); return 1; }
    auto* s = reinterpret_cast<const ShmLcMap*>(p);

    // 预热：触发页加载 & 分支预测/缓存热身
    ShmLcMap warm{};
    (void)seqlock_snapshot(s, warm);
    (void)to_logic_now(warm, (uint64_t)TscSource::now_ns());

    std::vector<uint64_t> dts;
    dts.reserve(N);

    // 正式测量
    ShmLcMap snap{};
    int64_t  last_L = 0;
    for (int i=0; i<N; ++i) {
        // 起始打点（用同域 TSC）
        uint64_t t0 = (uint64_t)TscSource::now_ns();

        // ① seqlock 快照读共享内存
        seqlock_snapshot(s, snap);
        // ② 取一次“与写端同域的 R_now”
        const uint64_t R_now = (uint64_t)TscSource::now_ns();
        // ③ 线性换算得到 L_now
        last_L = to_logic_now(snap, R_now);

        // 结束打点
        uint64_t t1 = (uint64_t)TscSource::now_ns();
        dts.push_back(t1 - t0);
    }

    // 统计（避免测量过程中频繁 I/O）
    std::sort(dts.begin(), dts.end());
    double p50 = percentile(dts, 0.50);
    double p90 = percentile(dts, 0.90);
    double p99 = percentile(dts, 0.99);
    uint64_t min = dts.front();
    uint64_t max = dts.back();
    long double sum = 0.0L;
    for (auto v : dts) sum += (long double)v;
    double avg = (double)(sum / (long double)dts.size());

    // 打印一次锚点信息 + 统计
    std::printf("domain=TSC | R_anchor=%" PRIu64 " ns, L_anchor=%" PRId64 " ns, s=% .9f, ver=%u\n",
                warm.R_anchor_ns, warm.L_anchor_ns, warm.s_ns_per_ns, warm.version);
    std::printf("Sample L_now=%" PRId64 " (last)\n", last_L);
    std::printf("N=%d | dt(ns): min=%" PRIu64 ", p50=%.1f, p90=%.1f, p99=%.1f, max=%" PRIu64 ", avg=%.1f\n",
                N, min, p50, p90, p99, max, avg);

    munmap((void*)p, sizeof(ShmLcMap));
    close(fd);
    return 0;
}
